package asf.core
{
	import asf.controllers.FrontController;
	import asf.events.ApplicationLoadProgressEvent;
	import asf.events.NavigationEvent;
	import asf.interfaces.ISectionManagement;
	import asf.interfaces.ITransition;
	import asf.log.LogLevel;
	import asf.resources.URLResources;
	import asf.sections.DefaultSectionManager;
	import asf.sections.Popup;
	import asf.sections.Section;
	
	import flash.display.Sprite;
	import flash.events.ContextMenuEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.ui.ContextMenu;
	import flash.ui.ContextMenuItem;
	import flash.utils.Dictionary;
	
	[Event( type="asf.events.NavigationEvent", name="changed" ) ]
	[Event( type="asf.events.NavigationEvent", name="sectionClosed" ) ]
	
	internal class Navigation extends EventDispatcher
	{
		private var _sections:Array;
		private var _sectionsByID:Object;
		private var _sectionsIDs:Array;
		
		private var config:XML;
		private var app:Application;
		
		private var currentSection:Section;
		private var _baseLayer:Sprite;
		
		private var menu:ContextMenu = new ContextMenu( );
		private var menuDict:Dictionary;
		
		private var _defaultTransition:ITransition;
		private var _defaultOutTransition:ITransition;
		private var userTransition:ITransition;
		
		private var _history:History;
		
		private var _sectionsManager:ISectionManagement;
		
		public function Navigation( p_configXML:XML, p_app:Application )
		{
			_sections = new Array( );
			_sectionsByID = new Object( );
			_sectionsIDs = new Array( );
			
			_sectionsManager = new DefaultSectionManager( p_app );
			
			_history = new History( this );
			
			config = p_configXML;
			app = p_app;
			
			init( );
		}
		
		public function getURLLinkByLevel( level:int = 1 ):String
		{
			return URLResources.getLinkByLevel( level );
		}
		
		public function getURLLinkByIndex( index:int = 0 ):String
		{
			return URLResources.getLinkByIndex( index );
		}
		
		public function getURLQueryStringParam( paramName:String ):String
		{
			return URLResources.getQueryStringParam( paramName );
		}
		
		public function getURLLastDeepLink( ):String
		{
			return URLResources.getLastLink( );
		}
		
		public function get deepLinkNumLevels( ):int
		{
			return URLResources.numLevels;
		}
		
		public function get fullDeepLink( ):String
		{
			return URLResources.link;
		}
		
		public function formatNoCacheURL( fileURL:String ):String
		{
			return URLResources.noCacheFile( fileURL );
		}
		
		public function dispose( ):void
		{
			closeCurrentSection( );
			closePopups( );
			sectionsManager.dispose( );
			
			var section:Section;
			
			for each( section in _sections )
			{
				section.view.dispose( );
			}
			
			_sections = null;
			_sectionsByID = null;
			_sectionsIDs = null;
			
			_sectionsManager = null;
			
			app = null;
			config = null;
			
			_history.dispose( );
			_history = null;
		}
		
		public function set sectionsManager( value:ISectionManagement ):void
		{
			_sectionsManager = value; 
		}
		
		public function get sectionsManager( ):ISectionManagement
		{
			return _sectionsManager;
		}
		
		public function get history( ):History
		{
			return _history;
		}
		
		private function init( ):void
		{
			var sectionsXML:XMLList = config.sections.section;
			var sectionXML:XML;
			var section:Section;
			
			var menuItem:ContextMenuItem;
			
			menuDict = new Dictionary( );
			
			try
			{
				_baseLayer = app.layers[ config.sections.@layer ];
			}
			catch( e:Error )
			{
				_baseLayer = app.container;
			}
			
			for each( sectionXML in sectionsXML )
			{
				section = new Section( sectionXML, app );
				
				menuItem = new ContextMenuItem( section.menuLabel || section.id );
				menuDict[ menuItem ] = section;
				
				menu.customItems.push( menuItem );
				
				menuItem.addEventListener( ContextMenuEvent.MENU_ITEM_SELECT, contextMenuClickHandler );
				
				_sections.push( section );
				_sectionsByID[ section.id ] = { section: section, index: _sections.length - 1 };
				_sectionsIDs.push( section.id );
			}
			
			menu.hideBuiltInItems( );
			
			if( app.parameters.useContextMenu == "true" )
			{
				var prevItem:ContextMenuItem = new ContextMenuItem( "<<", true );
				var nextItem:ContextMenuItem = new ContextMenuItem( ">>" );
				
				prevItem.addEventListener( ContextMenuEvent.MENU_ITEM_SELECT, nextPrevClickHandler );
				nextItem.addEventListener( ContextMenuEvent.MENU_ITEM_SELECT, nextPrevClickHandler );
				
				menu.customItems.push( prevItem, nextItem );
				
				app.container.stage.showDefaultContextMenu = false;
				app.container.contextMenu = menu;
				
			}
		}
		
		public function getContextMenu( ):ContextMenu
		{
			return menu;
		}
		
		private function nextPrevClickHandler( evt:ContextMenuEvent ):void
		{
			var item:ContextMenuItem = evt.target as ContextMenuItem;
			
			switch( item.caption )
			{
				case "<<":
				{
					history.back( );
					break;
				}
				case ">>":
				{
					history.next( );
					break;
				}
			}
		}
		
		public function get sectionsIDs( ):Array
		{
			var section:Section;
			var n:uint = 0;
			var id:String;
			
			_sectionsByID = new Object( );
			
			for each( section in _sections )
			{
				id = section.id;
				
				_sectionsIDs[ n ] = id;
				_sectionsByID[ id ] = { section: section, index: n };
				
				n++;
			}
			
			return _sectionsIDs.concat( );
		}
		
		public function set defaultOutTransition( value:ITransition ):void
		{
			_defaultOutTransition = value;
		}
		
		public function get defaultOutTransition( ):ITransition
		{
			return _defaultOutTransition;
		}
		
		public function set defaultTransition( value:ITransition ):void
		{
			_defaultTransition = value;
		}
		
		public function get defaultTransition( ):ITransition
		{
			return _defaultTransition;
		}
		
		private function contextMenuClickHandler( evt:ContextMenuEvent ):void
		{
			changeTo( menuDict[ evt.target ], userTransition || defaultTransition );
		}
		
		public function get baseLayer( ):Sprite
		{
			return _baseLayer;
		}
		
		public function get sections( ):Array
		{
			return _sections.concat( );
		}
		
		public function getSection( section:* ):Section
		{
			if( section is Section ) return section;
			return getSectionByID( section as String );
		}
		
		public function getSectionByID( sectionID:String ):Section
		{
			var tmp:Array = sectionsIDs;
			return _sectionsByID[ sectionID ].section;
		}
		
		public function getSectionByIndex( index:uint ):Section
		{
			return _sections[ index ];
		}
		
		public function removeSection( section:Section ):void
		{
			var id:String = section.id;
			
			_sections.splice( _sectionsByID[ id ].index, 1 ); 
			delete _sectionsByID[ id ];
			
			section.dispose( );
		}
		
		public function removeSectionByID( sectionID:String ):void
		{
			removeSection( _sectionsByID[ sectionID ].section );
		}
		
		public function changeTo( section:*, transition:ITransition = null, transitionOut:ITransition = null, initObject:Object = null, registerHistory:Boolean = true ):Section
		{
			currentSection = ( ! ( section is Section ) )? getSectionByID( String( section ) ): section;
			pauseBackground( currentSection );
			
			app.log( "changing to: " + currentSection.id, LogLevel.INFO_3 ); 
			
			currentSection.addEventListener( ApplicationLoadProgressEvent.DEPENDENCIES_LOAD_COMPLETE, onSectionComplete );
			
			sectionsManager.changeTo( section, transition, transitionOut, initObject );
			
			if( registerHistory )
			{
				history.cut( );
				history.push( section, transition, transitionOut, initObject );
			}
			
			closePopups( );
			FrontController.broadcastEvent( AppGlobalCommands.NAVIGATION_CHANGED );
			
			dispatchEvent( new NavigationEvent( NavigationEvent.CHANGED ) );
			
			return currentSection;
		}
		
		private function onSectionComplete( evt:Event ):void
		{
			var curSection:Section = evt.target as Section;
			var section:Section;
			var ar:Array = this.sections;
			
			curSection.removeEventListener( ApplicationLoadProgressEvent.DEPENDENCIES_LOAD_COMPLETE, onSectionComplete );
			
			app.log( "load complete: " + curSection.id, LogLevel.INFO_3 );
			
			for each( section in ar )
			{
				if( section.isLoadingInBackground )
				{
					section.resumeLoading( );
				}
			}
		}
		
		private function pauseBackground( exceptSection:Section ):void
		{
			var section:Section;
			var ar:Array = this.sections;
			
			app.log( "paused", LogLevel.INFO_3 );
			
			for each( section in ar )
			{
				if( section != exceptSection && section.isLoadingInBackground )
				{
					section.pauseLoading( );
				}
			}
		}
		
		private function closePopups( ):void
		{
			var pop:Popup;
			var pops:Array = app.popups.items;
			
			app.log( "", LogLevel.INFO_3 );
			
			for each( pop in pops )
			{
				if( pop.closeOnNavigate )
				{
					app.popups.closePopup( pop );
				}
			}
		}
		
		public function getCurrentSection( ):Section
		{
			return currentSection;
		}
		
		public function closeCurrentSection( ):void
		{
			app.log( "", LogLevel.INFO_3 );
			
			dispatchEvent( new NavigationEvent( NavigationEvent.SECTION_CLOSED ) );
			
			sectionsManager.closeCurrentSection( );
			currentSection = null;
		}
		
	}
}